%%% Field.oz
%
% This module defines the basic data structures for a minesweeper
% game.
%
% Given a field with NR rows and NC columns, two systems of
% coordinates on the field are used:
%  - R#C (row-column) with 1 =< R =< NR and 1 =< C =< NC
%  - I (element index) width 1 =< I =< NR*NC
% The elements of the field are numbered row by row.  Both systems are
% related by the equation I = (R-1) * NC + C.
%
% The distance between coordinates is the infinite one, i.e.,
% distance(R1#C1, R2#C2) = max(|R1-R2|, |C1-C2|).  This distance is
% used to define neighborhoods.
%
% {Field.coord NR#NC ?U}
%    creates functions to manipulate coordinates in a NR#NC field.
%    U is coord(toI:   ToI
%               toRC:  ToRC
%               boxI:  BoxI
%               boxRC: BoxRC)
%     - {ToI R#C $} returns the I coordinate corresponding to R#C
%     - {ToRC I $} returns the R#C coordinates corresponding to I
%     - {BoxI I D $} returns the list of coordinates at distance =< D
%     - {BoxRC R#C D $} idem, for R#C coordinates
%
%
% A field is represented by a tuple of size NR*NC.  The "index"
% coordinate system is used to access elements.
%
% Two representations are used for a field's elements: a binary one,
% and a symbolic one.  In the binary representation, 0 (resp. 1)
% denotes the absence (resp. presence) of a mine.  In the symbolic
% representation, safe(K) indicates the absence of a mine, with K
% mines in the neighborhood; while mine(X) indicates a mine; X might
% be used to tell that the mine has exploded.
%
% {Field.new NR#NC ?F}
%    creates a field F to be filled with values
%
% {Field.random NR#NC NMines ?BF}
%    creates a random binary field BF with NMines mines
%
% {Field.binary Sym ?Bin}
% {Field.symbolic Bin ?Sym}
%    converts the symbolic (resp. binary) representation into its
%    equivalent binary (resp. symbolic) representation
%
functor
import
   OS(rand srand)

export
   Coord
   new:NewField
   Random
   Binary Symbolic

define
   % instanciates useful functions related to coordinates
   fun {Coord NR#NC}
      fun {ToI R#C}
	 (R-1) * NC + C
      end
      fun {ToRC I}
	 ((I-1) div NC + 1)#((I-1) mod NC + 1)
      end
      fun {BoxI I D}
	 {Map {BoxRC {ToRC I} D} ToI}
      end
      fun {BoxRC R#C D}
	 for I in {Max 1 R-D}..{Min R+D NR}  collect:Coll do
	    for J in {Max 1 C-D}..{Min C+D NC} do {Coll I#J} end
	 end
      end
   in
      coord(toI:   ToI
	    toRC:  ToRC
	    boxI:  BoxI
	    boxRC: BoxRC)
   end

   % creates an empty field
   fun {NewField NR#NC}
      {Tuple.make field NR*NC}
   end

   % creates a random binary field of size NRxNC, with NMines
   fun {Random NR#NC NMines}
      {List.toTuple field {Random01 NR*NC NMines}}
   end

   % Return a random list of length N, with N1 1's and N-N1 0's
   {OS.srand 0}
   fun {Random01 N N1}
      if N==0 then nil
      elseif {OS.rand} mod N < N1
      then 1|{Random01 N-1 N1-1}
      else 0|{Random01 N-1 N1}
      end
   end

   % converts between the binary and symbolic representations
   fun {Binary X}
      case X of safe(_) then 0 [] mine(_) then 1 end
   end
   fun {Symbolic X}
      case X of 0 then safe(_) [] 1 then mine(_) end
   end
end
